package top.porchwood.jweb.mcsm.struct;

import com.google.gson.Gson;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import top.porchwood.jweb.mcsm.service.MCSMUserDetailService;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.sql.Date;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * Description:
 *
 * @Author 一七年夏
 * @Link https://space.bilibili.com/16869954
 * @Email 2098833867@qq.com
 * @License GNU General Public Liscene 3.0
 * @DateTime 2021-08-01 19:21
 */
public class MCSMUserDetails implements UserDetails, Serializable {
    private static final Logger logger = LogManager.getLogger();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }


    private String UUID;
    private String NAME;
    private String NICK = null;
    private String REG_IP;
    private long REG_TIME = Calendar.getInstance().getTime().getTime();
    private String LOG_IP;
    private long LOG_TIME = REG_TIME;
    private String PASSWORD;
    private String PERMISSION = "[]";
    private short ACCOUNT_EXPIRED = 0;
    private short LOCKED = 0;
    private short CREDENTIALS_EXPIRED = 0;
    private short ENABLED = 1;
    private String EMAIL;

    private MCSMUserDetails() {
    }

    private MCSMUserDetails(String userName, String password, String ip) {
        this.NAME = userName;
        this.UUID = java.util.UUID.nameUUIDFromBytes(userName.getBytes(StandardCharsets.UTF_8)).toString();
        this.PASSWORD = password;
        this.LOG_IP = this.REG_IP = ip;
    }

    @NonNull
    public static MCSMUserDetails createUser(
            @NonNull String userName,
            @NonNull String rawPassword,
            @NonNull String ip)
            throws UserHasRegisteredException, UserNameSyntaxException {
        if (userName.contains("'")) throw new UserNameSyntaxException(userName);
        JdbcTemplate template = MCSMUserDetailService.getInstance().getJdbc();
        PasswordEncoder pe = MCSMUserDetailService.getInstance().getPasswordEncoder();
        MCSMUserDetails user = new MCSMUserDetails(userName, pe.encode(rawPassword), ip);
        try {
            template.queryForObject("select NAME from cbmc_user where NAME='%s'".formatted(userName), String.class);
            throw new UserHasRegisteredException();
        } catch (EmptyResultDataAccessException ignored) {
        }
        template.update("""
                        insert into cbmc_user(
                            UUID, NAME, NICK, REG_IP, REG_TIME, LOG_IP,
                            LOG_TIME, PASSWORD, PERMASSION
                        ) values(?,?,?,?,?,?,?,?,?);
                        """,
                user.UUID, user.NAME, user.NICK, user.REG_IP, user.REG_TIME, user.LOG_IP,
                user.LOG_TIME, user.PASSWORD, user.PERMISSION
        );
        return user;
    }

    public static class UserHasRegisteredException extends Exception {

    }

    @NonNull
    @Override
    public String getPassword() {
        return PASSWORD;
    }

    @NonNull
    @Override
    public String getUsername() {
        return NAME;
    }

    @Override
    public boolean isAccountNonExpired() {
        return ACCOUNT_EXPIRED != 1;
    }

    @Override
    public boolean isAccountNonLocked() {
        return LOCKED != 1;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return CREDENTIALS_EXPIRED != 1;
    }

    @Override
    public boolean isEnabled() {
        return ENABLED == 1;
    }

    @NonNull
    public String getUUID() {
        return UUID;
    }

    @Nullable
    public String getNick() {
        return NICK;
    }

    public void setNick(@Nullable String nick) {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set NICK=? where UUID='%s'".formatted(UUID), nick);
        this.NICK = nick;
    }

    @NonNull
    public String getRegisterIP() {
        return REG_IP;
    }

    @NonNull
    public Timestamp getRegisterTime() {
        return new Timestamp(REG_TIME);
    }

    @NonNull
    public String getLastLoginIP() {
        return LOG_IP;
    }

    public void setLastLoginIP(@NonNull String ip) {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set LOG_IP='%s' where UUID='%s'".formatted(ip, UUID));
        this.LOG_IP = ip;
    }

    @NonNull
    public Date getLastLoginTime() {
        return new Date(LOG_TIME);
    }

    public void setLastLoginTime(@NonNull Date time) {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set LOG_TIME='%d' where UUID='%s'".formatted(time.getTime(), UUID));
        this.LOG_TIME = time.getTime();
    }

    public void setPassword(@NonNull String password) {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set PASSWORD='%s' where UUID='%s'".formatted(password, UUID));
        this.PASSWORD = password;
    }

    @NonNull
    public String getPermission() {
        return PERMISSION;
    }

    public void setPermissions(@NonNull String permissions) {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set PERMASSION='%s' where UUID='%s'".formatted(permissions, UUID));
        this.PERMISSION = permissions;
    }

    public void expireAccount() {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set ACCOUNT_EXPIRED=1 where UUID='%s'".formatted(UUID));
        this.ACCOUNT_EXPIRED = 1;
    }

    public void disExpireAccount() {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set ACCOUNT_EXPIRED=0 where UUID='%s'".formatted(UUID));
        this.ACCOUNT_EXPIRED = 0;
    }

    public void lockAccount() {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set LOCKED=1 where UUID='%s'".formatted(UUID));
        this.LOCKED = 1;
    }

    public void disLockAccount() {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set LOCKED=0 where UUID='%s'".formatted(UUID));
        this.LOCKED = 0;
    }

    public void expireCredentials() {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set CREDENTIALS_EXPIRED=1 where UUID='%s'".formatted(UUID));
        this.CREDENTIALS_EXPIRED = 1;
    }

    public void disExpireCredentials() {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set CREDENTIALS_EXPIRED=0 where UUID='%s'".formatted(UUID));
        this.CREDENTIALS_EXPIRED = 0;
    }

    public void disableAccount() {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set ENABLED=0 where UUID='%s'".formatted(UUID));
        this.ENABLED = 0;
    }

    public void enableAccount() {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set ENABLED=1 where UUID='%s'".formatted(UUID));
        this.ENABLED = 1;
    }

    public static class UserNameSyntaxException extends Throwable {
        String username;

        UserNameSyntaxException(String username) {
            this.username = username;
        }

        @Override
        public String getMessage() {
            return "“%s”不是一个可用的用户名".formatted(username);
        }
    }

    public static MCSMUserDetails toObject(Map map) {
        //时间要转为long
        map.put("REG_TIME", ((LocalDateTime) map.get("REG_TIME")).toEpochSecond(ZoneOffset.UTC));
        map.put("LOG_TIME", ((LocalDateTime) map.get("LOG_TIME")).toEpochSecond(ZoneOffset.UTC));
        return new Gson().fromJson(new Gson().toJson(map), MCSMUserDetails.class);
    }

    @Nullable
    public String getEmail() {
        return EMAIL;
    }

    public void setEmail(@Nullable String email) {
        JdbcTemplate jt = MCSMUserDetailService.getInstance().getJdbc();
        jt.update("update cbmc_user set EMAIL='%s' where UUID='%s'".formatted(email, UUID));
        this.EMAIL = email;
    }
}
